package com.managertrade.job;

import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.managertrade.dao.sqlite.entity.KlineWithIndicatorsEntity;
import com.managertrade.service.sqllite.service.KlineWithIndicatorsService;
import com.managertrade.util.BackpackApiClientRequest;
import com.managertrade.util.strategy.client.BackpackTradingBot;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import java.math.RoundingMode;
import java.util.*;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.concurrent.atomic.AtomicLong;

@Component
@Slf4j
public class BackPackJob {
    @Autowired
    private ObjectMapper mapper ;
    // 上一次方向
    private String lastDirection = null;
    private final  String direction = "buy";
    private final  BigDecimal quantity = new BigDecimal(5);
    private final String contractId ="ASTER_USDC_PERP";
    private static final AtomicLong taskCount = new AtomicLong(0);

    private final int maxSize = 100000;  // 最大保存条数
    private final LinkedList<BigDecimal> fundsList = new LinkedList<>();

    @Autowired
    KlineWithIndicatorsService klineWithIndicatorsService;
    /**
     * 添加新的资金数据
     */
    public void add(BigDecimal value) {
        fundsList.add(value);
        if (fundsList.size() > maxSize) {
            fundsList.removeFirst(); // 超过最大长度 → 删除最早数据
        }
    }

    /**
     * 获取当前窗口数据（按时间顺序）
     */
    public List<BigDecimal> getFundsList() {
        return fundsList;
    }
    //@Scheduled(cron = "0 */1 * * * ?")
    public void execTask(){
        try {
            log.info("===== 第 {} 次执行定时任务 =====", taskCount.incrementAndGet());

            // 1️⃣ 获取账户资金（或净资产）
            BigDecimal totalFunds = BigDecimal.ZERO;
            BackpackTradingBot.BackpackPosition positionInfoNow = getCurrentPosition();
            if (positionInfoNow != null) {
                totalFunds = new BigDecimal(positionInfoNow.getNetExposureNotional());
                add(totalFunds); // 记录资金序列
            }

            PredictionResult predictionResult = predictionResult();
            String newDirection =   predictionResult.getExecutedSignal();

            log.info("当前时间: {}, 当前总资产: {}, 资金序列: {}, 当前方向预测: {}",
                    DateUtil.format(DateUtil.date(), "yyyy-MM-dd HH:mm:ss"),
                    totalFunds,
                    JSONUtil.toJsonStr(getFundsList()),
                    newDirection
            );

            // 4️⃣ 查询当前持仓
            BackpackTradingBot.BackpackPosition positionInfo = getCurrentPosition();
            BigDecimal currentPosition = positionInfo != null
                    ? new BigDecimal(positionInfo.getNetQuantity())
                    : BigDecimal.ZERO;

            // 5️⃣ 判断操作逻辑
            if (positionInfo == null || currentPosition.compareTo(BigDecimal.ZERO) == 0) {
                // 无持仓
                if (!"hold".equalsIgnoreCase(newDirection)) {
                    BackpackTradingBot.OrderResult order = placeOpenOrder(quantity.abs(), newDirection); // 全仓开仓
                    if (order != null) {
                        log.info("开仓: {} → {}", totalFunds, newDirection);
                    }
                } else {
                    log.info("无持仓且方向为 hold，暂不操作");
                }
            } else if ((currentPosition.compareTo(BigDecimal.ZERO) > 0 && "sell".equalsIgnoreCase(newDirection)) ||
                    (currentPosition.compareTo(BigDecimal.ZERO) < 0 && "buy".equalsIgnoreCase(newDirection))) {
                // 持仓方向与预测方向相反 → 平仓再开仓
                BigDecimal closeQty = currentPosition.abs();
                String closeDirection = currentPosition.compareTo(BigDecimal.ZERO) > 0 ? "sell" : "buy";

                BackpackTradingBot.OrderResult closeOrder = placeOpenOrder(closeQty, closeDirection);
                if (closeOrder != null) {
                    log.info("平仓完成: {} → {}", closeQty, closeDirection);
                }

                // 全仓开新仓
                BackpackTradingBot.OrderResult newOrder = placeOpenOrder(totalFunds.abs(), newDirection);
                if (newOrder != null) {
                    log.info("反向全仓开仓: {} → {}", totalFunds, newDirection);
                }
            } else {
                // 持仓方向与预测方向一致 → 不操作
                log.info("持仓方向与预测方向一致，无需操作，持仓数量: {}", currentPosition);
            }

            lastDirection = newDirection;

        } catch (Exception e) {
            log.error("Trade loop error: " + e.getMessage(), e);
        }
    }
    /**
     * 根据资金趋势策略预测下一步方向
     * @param fundsList 资金序列
     * @param thresholdPct 阈值百分比，例如 0.001 = 0.1%
     * @param accumulationCount 连续趋势根数
     * @return "涨" 或 "跌"
     */
    public static String predictNext(List<BigDecimal> fundsList, double thresholdPct, int accumulationCount) {
        if (fundsList == null || fundsList.size() < 4) {
            return "hold"; // 数据太少，不交易
        }

        int currentDirection = 0; // 0=未定, 1=buy, -1=sell
        int trendCount = 0;

        // ① 计算平均波动
        BigDecimal avgChange = BigDecimal.ZERO;
        for (int i = 1; i < fundsList.size(); i++) {
            avgChange = avgChange.add(fundsList.get(i).subtract(fundsList.get(i - 1)).abs());
        }
        avgChange = avgChange.divide(BigDecimal.valueOf(fundsList.size() - 1), 8, RoundingMode.HALF_UP);

        // ② 自适应手续费（噪音过滤）
        BigDecimal feeThresholdDynamic = avgChange.multiply(BigDecimal.valueOf(0.3)); // 高频放小
        if (feeThresholdDynamic.compareTo(BigDecimal.valueOf(0.00001)) < 0) {
            feeThresholdDynamic = BigDecimal.valueOf(0.00001);
        }

        BigDecimal lastDiff = BigDecimal.ZERO;
        int smallMoveCount = 0;

        // ③ 主循环：趋势检测
        for (int i = 1; i < fundsList.size(); i++) {
            BigDecimal prev = fundsList.get(i - 1);
            BigDecimal curr = fundsList.get(i);
            BigDecimal diff = curr.subtract(prev);

            // 固定阈值 + 动态阈值
            BigDecimal fixedThreshold = prev.multiply(BigDecimal.valueOf(thresholdPct * 0.05)); // 高频降低阈值
            BigDecimal dynamicThreshold = avgChange.multiply(BigDecimal.valueOf(0.5));
            BigDecimal threshold = fixedThreshold.max(dynamicThreshold);

            // 小幅波动统计
            if (diff.abs().compareTo(feeThresholdDynamic) < 0) {
                smallMoveCount++;
            }

            // 判断趋势（去掉加速度，更简单）
            if (diff.compareTo(threshold) > 0) {
                trendCount = Math.min(trendCount + 1, accumulationCount * 3);
            } else if (diff.compareTo(threshold.negate()) < 0) {
                trendCount = Math.max(trendCount - 1, -accumulationCount * 3);
            } else {
                trendCount = (int)(trendCount * 0.7); // 缓慢衰减
            }

            // 更新方向
            if (trendCount >= accumulationCount) currentDirection = 1;
            else if (trendCount <= -accumulationCount) currentDirection = -1;

            lastDiff = diff;
        }

        // ④ 高频震荡过滤放宽
        double smallMoveRatio = (double) smallMoveCount / (fundsList.size() - 1);
        double holdThreshold = 0.85; // 高频允许更多小波动
        if (smallMoveRatio > holdThreshold) return "hold";

        // ⑤ 输出信号
        if (currentDirection == 1) return "buy";
        if (currentDirection == -1) return "sell";
        return "hold";
    }
    public  PredictionResult predictionResult() {
        List<KlineWithIndicatorsEntity> latestBySymbolAndPeriod = klineWithIndicatorsService.findLatestBySymbolAndPeriod("ASTERUSDT","1m");
        PredictionResult predictionResult = predictLatestSignal(latestBySymbolAndPeriod, 100, 0);
        return predictionResult;
    }
    /**
     * 根据区间策略计算预测方向
     *
     * @param klines       K线列表（按时间升序排列）
     * @param index        当前 K线索引
     * @param rangePeriod  区间长度，例如最近100根
     * @param lastSignal   上一次执行信号（"buy" 或 "sell"），用于翻转逻辑，可传 null
     * @param consecutiveWrongCount 上一次连续错误次数
     * @return 预测结果对象，包括 executedSignal 和是否翻转
     */
    // ==== 全局状态变量 ====
    private static String lastSignal = null;       // 上一次执行信号 buy/sell
    private static int consecutiveWrongCount = 0;  // 上一次连续错误次数

    /**
     * 根据最新 K 线数据预测方向（自动管理上一次信号和连续错误次数）
     *
     * @param klines      K 线列表（按时间升序排列）
     * @param rangePeriod 区间长度，例如最近100根
     * @param futureWindow 用于判断未来走势的窗口长度，可传 0 表示不判断
     * @return PredictionResult，包含 executedSignal 和是否翻转
     */
    public static PredictionResult predictLatestSignal(List<KlineWithIndicatorsEntity> klines,
                                                       int rangePeriod,
                                                       int futureWindow) {
        if (klines.size() < rangePeriod + 1) {
            return new PredictionResult("hold", false); // 数据不足，保持观望
        }

        int index = klines.size() - 1; // 最新一根 K 线
        KlineWithIndicatorsEntity k = klines.get(index);

        // 最近 rangePeriod 根区间（不包含最新）
        List<KlineWithIndicatorsEntity> rangeList = klines.subList(index - rangePeriod, index);
        double highest = rangeList.stream().mapToDouble(KlineWithIndicatorsEntity::getHigh).max().orElse(0);
        double lowest = rangeList.stream().mapToDouble(KlineWithIndicatorsEntity::getLow).min().orElse(0);
        double mid = (highest + lowest) / 2.0;

        double close = k.getClose();
        String signal;

        // ===== 区间策略 =====
        if (close > highest) {
            signal = "buy";
        } else if (close < lowest) {
            signal = "sell";
        } else if (close >= mid && close <= highest) {
            signal = "hold";
        } else {
            signal = "buy";
        }

        // hold 不翻转
        if (signal.equals("hold")) {
            return new PredictionResult("hold", false);
        }

        // ===== 连续错误翻转逻辑 =====
        boolean flipped = false;
        String executedSignal = signal;
        if (lastSignal != null && consecutiveWrongCount >= 2 && signal.equals(lastSignal)) {
            executedSignal = signal.equals("buy") ? "sell" : "buy";
            flipped = true;
        }

        // ===== 可选未来走势验证（更新全局连续错误次数） =====
        if (futureWindow > 0 && index + futureWindow < klines.size()) {
            double futureAvg = 0;
            for (int j = 1; j <= futureWindow; j++) {
                futureAvg += klines.get(index + j).getClose();
            }
            futureAvg /= futureWindow;

            boolean correctSignal = (executedSignal.equals("buy") && futureAvg > close)
                    || (executedSignal.equals("sell") && futureAvg < close);

            // 更新全局状态
            if (correctSignal) {
                lastSignal = executedSignal;
                consecutiveWrongCount = 0;
            } else {
                if (executedSignal.equals(lastSignal)) {
                    consecutiveWrongCount++;
                } else {
                    lastSignal = executedSignal;
                    consecutiveWrongCount = 1;
                }
            }
        } else {
            // 没有未来窗口时，不更新连续错误计数，但保留 lastSignal 用于下一次翻转
            lastSignal = executedSignal;
        }

        return new PredictionResult(executedSignal, flipped);
    }


    /**
     * 预测结果封装类
     */
    public static class PredictionResult {
        private final String executedSignal; // 执行信号 buy/sell/hold
        private final boolean flipped;       // 是否翻转

        public PredictionResult(String executedSignal, boolean flipped) {
            this.executedSignal = executedSignal;
            this.flipped = flipped;
        }

        public String getExecutedSignal() {
            return executedSignal;
        }

        public boolean isFlipped() {
            return flipped;
        }

        @Override
        public String toString() {
            return "PredictionResult{" +
                    "executedSignal='" + executedSignal + '\'' +
                    ", flipped=" + flipped +
                    '}';
        }
    }

    public BackpackTradingBot.OrderResult placeOpenOrder(BigDecimal quantity, String direction) {
        try {
            List<Map<String, Object>> bodyList = new ArrayList<>();
            Map<String, Object> order = new HashMap<>();
            order.put("orderType", "Market");            // 市价开仓
            order.put("symbol", contractId);     // 交易对
            if("buy".equalsIgnoreCase(direction)){
                // 买入（Bid）
                order.put("side", "Bid");
            }else {
                order.put("side", "Ask");
            }
            order.put("quantity", quantity);
            bodyList.add(order);
            String respText  = new BackpackApiClientRequest(false).post( bodyList);
            List<BackpackTradingBot.OrderResult> orders = mapper.readValue(respText, new TypeReference<List<BackpackTradingBot.OrderResult>>() {});
            return orders.get(0);
        } catch (Exception e) {
            log.error("Open order error: " + e.getMessage());
            return null;

        }
    }
    public BackpackTradingBot.BackpackPosition getCurrentPosition() throws Exception {
        BackpackApiClientRequest client = new BackpackApiClientRequest(false);
        String openOrders = client.getCurrentPosition(contractId);
        if(!StringUtils.isEmpty(openOrders)){
            List<BackpackTradingBot.BackpackPosition> positions = mapper.readValue(openOrders, new TypeReference<List<BackpackTradingBot.BackpackPosition>>() {});
            if(!CollectionUtils.isEmpty(positions)){
                return positions.get(0);
            }
        }
        return null;
    }
}
